/*
 *
 *  Copyright (C) 2010 Tal Eisenberg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package io.midi;

import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;

/**
 *
 * @author eisental
 */
public class GenericMidiController extends MidiSystemController implements Receiver {
    private MidiDevice device = null;
    private Receiver outreceiver = null;

    @Override
    public void open() throws IllegalArgumentException {
        if (portId==-1)
            throw new IllegalArgumentException("Trying to open midi port while port id is not set.");
        try {
            if (portId>=MidiSystem.getMidiDeviceInfo().length)
                throw new IllegalArgumentException("Midi port id " + portId + " doesn't exist.");
            device = MidiSystem.getMidiDevice(MidiSystem.getMidiDeviceInfo()[this.portId]);
            device.open();
            if (isInput) {
                if (device.getMaxTransmitters()==0) {
                    device.close();
                    throw new IllegalArgumentException(device.getDeviceInfo() + " is not a MIDI In device");
                }
                device.getTransmitter().setReceiver(this);
            } else {
                if (device.getMaxReceivers()==0) {
                    device.close();
                    throw new IllegalArgumentException(device.getDeviceInfo() + " is not a MIDI Out device");
                }
                outreceiver = device.getReceiver();
            }
        } catch (MidiUnavailableException e) {
            throw new IllegalArgumentException("Can't open midi port: " + e.getMessage());
        }
    }

    @Override
    public void close() throws IllegalArgumentException {
        if (device!=null)
            device.close();
    }

    @Override
    public void sendMidiMessage(MidiMessage msg, long timestamp) throws IllegalArgumentException {
        if (isInput)
            throw new IllegalArgumentException("Trying to send midi message while controller is set to receive midi messages.");
        if (outreceiver==null)
            throw new IllegalArgumentException("Trying to send midi message before opening midi port.");
        outreceiver.send(msg, timestamp);
    }

    public void send(MidiMessage msg, long timestamp) {
        if (!isInput)
            throw new IllegalArgumentException("Received midi message from an output port.");
        listener.receivedMidi(msg, timestamp);
    }

    @Override
    public boolean isOpen() {
        return !(outreceiver==null);
    }

    @Override
    public String getPortName() {
        if (portId==-1) return "unknown";
        else return MidiSystem.getMidiDeviceInfo()[portId].getName();
    }

    @Override
    public int findPortByName(String name) {
        for (int i=0; i<MidiSystem.getMidiDeviceInfo().length; i++) {
            MidiDevice.Info info = MidiSystem.getMidiDeviceInfo()[i];
            if (info.getName().indexOf(name)!=-1)
                return i;
        }
        throw new IllegalArgumentException("Can't find id for port '" + name + "'");
    }

}
